package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.BeansException;
import cn.stimd.spring.beans.SimpleTypeConverter;
import cn.stimd.spring.beans.TypeConverter;
import cn.stimd.spring.beans.factory.BeanFactory;
import cn.stimd.spring.beans.factory.BeanFactoryUtils;
import cn.stimd.spring.beans.factory.FactoryBean;
import cn.stimd.spring.beans.factory.ObjectFactory;
import cn.stimd.spring.beans.factory.config.BeanDefinition;
import cn.stimd.spring.beans.factory.config.BeanPostProcessor;
import cn.stimd.spring.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringValueResolver;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    private BeanFactory parentBeanFactory;
    private ConversionService conversionService = new DefaultConversionService();
    private TypeConverter typeConverter = new SimpleTypeConverter();
    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
    //Bean的后处理器集合
    private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
    //字符串值解析器
    private final List<StringValueResolver> embeddedValueResolvers = new LinkedList<>();
    //缓存BeanDefinition
    private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);


    /**
     * 获取Bean
     * @param name          Bean的名称，可能带有&号
     * @param requiredType  Bean的类型
     */
    protected <T> T doGetBean(final String name, final Class<T> requiredType) {
        Object bean;
        String beanName = BeanFactoryUtils.transformedBeanName(name);

        //1. 从缓存中获取对象
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null) {
            //此时拿到的是FactoryBean，还需要获取目标对象（mbd参数为空，说明是检索操作）
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        //2. 缓存中不存在，则通过BeanDefinition来创建Bean，并加入缓存
        else {
            //2.1 检查父容器中是否存在Bean
            if(this.parentBeanFactory != null && !containsBeanDefinition(beanName)){
                return parentBeanFactory.getBean(beanName, requiredType);
            }

            //2.2 获取BeanDefinition
            final RootBeanDefinition mbd = getMergedBeanDefinition(beanName);
            //2.3 创建单例Bean并注册到容器中
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    try {
                        //模版方法，实际的创建逻辑由子类实现
                        return createBean(beanName, mbd);
                    }catch (Exception e){
                        throw new RuntimeException("创建Bean[" + beanName + "]失败", e);
                    }
                }
            });

            //2.4 如果是FactoryBean，则进一步获取目标Bean（mdb参数存在，说明是创建操作）
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }

        //3. 如果Bean不是requiredType的实例，则进行必要的类型转换
        if (requiredType != null && !requiredType.isInstance(bean)) {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        return (T) bean;
    }


    /**
     * 从Bean的实例中获取对象
     * @param beanInstance 表示传入的对象，可能是 FactoryBean，也可能是普通对象
     * @param name         表示查询的对象名（调用getBean方法传入的参数），可能是&foo，也可能是foo
     * @param beanName     真正的Bean的名称，固定为foo(实际上FactoryBean和目标对象在容器中存储的key都是foo，只是存储的地方不一样罢了)
     * @param mbd          如果为null说明是检索Bean，不为null则创建Bean
     */
    protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
        //1. 不允许普通Bean的name以&开头
        if(name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX) && !(beanInstance instanceof FactoryBean)){
            throw new BeansException("beanName[" + name + "]不是FactoryBean，实际类型为" + beanInstance.getClass() );
        }

        /*
         * 2. 只有当beanInstance是FactoryBean类型，且name也是foo（说明需要的是被FactoryBean包装的对象），才可以执行后续流程
         *
         * 左侧表达式说明是一个普通Bean，隐含条件“name不以&开头”已经在Step-1被过滤掉了。
         * 右侧表达式说明是一个FactoryBean，隐含条件“不是FactoryBean的实例”在左侧被过滤掉了
         */
        if (!(beanInstance instanceof FactoryBean) || name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return beanInstance;
        }

        //3. 使用工厂方法的方式获取Bean
        Object object = null;
        //mbd为空，说明是检索Bean的操作，只查询缓存即可
        if (mbd == null) {
            object = getCachedObjectForFactoryBean(beanName);
        }

        //4. 缓存中没找到，则创建目标Bean
        if(object == null){
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            object = getObjectFromFactoryBean(factory, beanName);
        }
        return object;
    }


    /**
     * 1、如果parent存在，则合并处理
     * 2、BeanFactory以RootBeanDefinition为标准，需要对其他类型的BeanDefinition进行转换
     */
    @Override
    public RootBeanDefinition getMergedBeanDefinition(String beanName) {
        if (!containsBeanDefinition(beanName) && getParentBeanFactory() != null){
            return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
        }

        RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        if(mbd == null){
            //查找原始的BeanDefinition缓存
            BeanDefinition bd = getBeanDefinition(beanName);
            if (bd instanceof RootBeanDefinition) {
                mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
            }else {
                mbd = new RootBeanDefinition(bd);
            }
            this.mergedBeanDefinitions.put(beanName, mbd);
        }
        return mbd;
    }


    /**
     * 推断Bean的类型，供getType和isTypeMatch方法调用。不处理FactoryBean，只获取原始Bean的类型
     * 这是一种简单实现，无法处理工厂方法和InstantiationAwareBeanPostProcessor
     * 基本上不使用，子类重写该方法
     *
     * @param typesToMatch 表示返回的Class不会暴露给外部
     */
    protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
        Class<?> targetType = mbd.getTargetType();
        if (targetType != null) {
            return targetType;
        }

        if (mbd.getFactoryMethodName() != null) {
            return null;
        }
        return resolveBeanClass(mbd, beanName, typesToMatch);
    }


    /**
     * 解析Bean的类型，并将解析后的Class存储在BeanDefinition中。
     * AnnotatedBeanDefinition的beanClass属性没有预先指定，需要解析
     * 比如ScannedGenericBeanDefinition的beanClass属性是String类型，需要转换成Class类型
     */
    protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch){
        try {
            //beanClass属性为Class类型，说明类型已确定，直接返回
            if(mbd.hasBeanClass()){
                return mbd.getBeanClass();
            }

            //beanClass属性为String类型，则通过反射处理
            String className = mbd.getBeanClassName();
            if(className != null){
                return ClassUtils.forName(className, this.beanClassLoader);
            }

            return mbd.resolveBeanClass(this.beanClassLoader);
        }catch (Exception e){
            throw new RuntimeException("解析BeanClass失败, beanName: " + beanName, e);
        }
    }


    protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
        Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
        return (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
    }

    protected void clearMergedBeanDefinition(String beanName) {
        this.mergedBeanDefinitions.remove(beanName);
    }

    //---------------------------------------------------------------------
    // 重写&实现
    //---------------------------------------------------------------------

    // ------------------------------- BeanFactory接口
    @Override
    public Object getBean(String name) throws RuntimeException {
        return doGetBean(name, null);
    }


    @Override
    public <T> T getBean(String name, Class<T> requiredType) {
        return doGetBean(name, requiredType);
    }


    @Override
    public Class<?> getType(String beanName) throws RuntimeException {
        //检查手动注册的Bean（没有BeanDefinition）
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null) {
            return beanInstance.getClass();
        }

        RootBeanDefinition mbd = getMergedBeanDefinition(beanName);
        return predictBeanType(beanName, mbd);
    }


    @Override
    public boolean isTypeMatch(String name, Class<?> typeToMatch) throws BeansException {
        return isTypeMatch(name, ResolvableType.forRawClass(typeToMatch));
    }

    /*
     * 检查BeanDefinition与指定类型是否匹配
     * 对于FactoryBean来说，没有指定BeanDefinition的beanClass属性，需要通过factoryMethodReturnType等其他属性来决定
     */
    @Override
    public boolean isTypeMatch(String name, ResolvableType typeToMatch) {
        String beanName = BeanFactoryUtils.transformedBeanName(name);
        Object beanInstance = getSingleton(beanName, false);

        //1. 单例已存在的情况
        //1.1 判断是否为FactoryBean
        if(beanInstance != null){
            if(!beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)){
                if(beanInstance instanceof FactoryBean){
                    return typeToMatch.isAssignableFrom(((FactoryBean) beanInstance).getObjectType());
                }
            }
            return typeToMatch.isInstance(beanInstance);
        }
        //1.2 注册了单例Bean，但没有对应的BeanDefinition（比如AnnotatedBeanDefinitionReader的register方法注册的实例）
        else if(containsSingleton(beanName) && !containsBeanDefinition(beanName)){
            return false;
        }

        //1.3 检查父容器
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            return parentBeanFactory.isTypeMatch(name, typeToMatch);
        }

        //2. 尚未注册单例Bean，这时需要从BeanDefinition中检索
        RootBeanDefinition mbd = getMergedBeanDefinition(beanName);
        Class<?> classToMatch = typeToMatch.resolve();
        if (classToMatch == null) {
            classToMatch = FactoryBean.class;
        }

        Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
                new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});

        //获取Bean的类型，以便进一步判断是否为FactoryBean
        Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
        if (beanType == null) {
            return false;
        }

        //2.1 如果是FactoryBean类型，但name不是以&开头，说明拿到的是包装的对象
        if (FactoryBean.class.isAssignableFrom(beanType)) {
            if (!BeanFactoryUtils.isFactoryDereference(name)) {
                beanType = getTypeForFactoryBean(beanName);
                if (beanType == null) {
                    return false;
                }
            }
        }
        //2.2 获取FactoryBean本身
        else if (BeanFactoryUtils.isFactoryDereference(name)) {
            beanType = predictBeanType(beanName, mbd, FactoryBean.class);
            if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
                return false;
            }
        }

        ResolvableType resolvableType = mbd.factoryMethodReturnType;
        if (resolvableType != null && resolvableType.resolve() == beanType) {
            return typeToMatch.isAssignableFrom(resolvableType);
        }
        return typeToMatch.isAssignableFrom(beanType);
    }


    //创建FactoryBean实例，从而得到包装对象的类型
    protected Class<?> getTypeForFactoryBean(String beanName) {
        FactoryBean<?> factoryBean = doGetBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName, FactoryBean.class);
        return getTypeForFactoryBean(factoryBean);
    }

    // ------------------------------- ConfigurableBeanFactory接口
    @Override
    public TypeConverter getTypeConverter() {
        return this.typeConverter;
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.add(beanPostProcessor);
    }

    @Override
    public int getBeanPostProcessorCount() {
        return this.beanPostProcessors.size();
    }

    @Override
    public void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException {
        this.parentBeanFactory = parentBeanFactory;
    }

    public BeanFactory getParentBeanFactory() {
        return this.parentBeanFactory;
    }

    @Override
    public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
        this.embeddedValueResolvers.add(valueResolver);
    }

    @Override
    public boolean hasEmbeddedValueResolver() {
        return !this.embeddedValueResolvers.isEmpty();
    }

    @Override
    public String resolveEmbeddedValue(String value) {
        if (value == null) {
            return null;
        }
        String result = value;
        for (StringValueResolver resolver : this.embeddedValueResolvers) {
            result = resolver.resolveStringValue(result);
            if (result == null) {
                return null;
            }
        }
        return result;
    }

    //---------------------------------------------------------------------
    // Getter&Setter
    //---------------------------------------------------------------------
    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }

    public ConversionService getConversionService() {
        return conversionService;
    }


    //---------------------------------------------------------------------
    // 模板方法
    //---------------------------------------------------------------------

    protected abstract Object createBean(String beanName, RootBeanDefinition mbd);
}
